#ifndef SIMPLE_SUPPORT_META_TYPELIST_HPP
#define SIMPLE_SUPPORT_META_TYPELIST_HPP

#include <cstddef> // std:size_t
#include <type_traits> // std::integral_constant std::enable_if_t std::is_same_v
#include <utility> // std::integer_sequence

namespace simple::support::meta
{

	template <typename List>
	struct list_size;
	template <template <typename...> class List, typename... Elements>
	struct list_size<List<Elements...>> :
		std::integral_constant<std::size_t, sizeof...(Elements)>
	{};
	template <typename List>
	constexpr auto list_size_v = list_size<List>::value;

	template <std::size_t Index, typename List, typename Enable = void>
	struct list_element;
	template <template <typename...> class List, typename First, typename... Rest>
	struct list_element<0, List<First, Rest...>>
	{ using type = First; };
	template <std::size_t Index, template <typename...> class List, typename First, typename... Rest>
	struct list_element<Index, List<First, Rest...>,
		std::enable_if_t<Index != 0>>
	{ using type = typename list_element<Index - 1, List<Rest...>>::type; };
	template <std::size_t Index, typename List>
	using list_element_t = typename list_element<Index, List>::type;

	template <typename List, typename Range>
	struct collect_list;
	template <template <typename...> class List, typename... Elements, std::size_t... indices>
	struct collect_list<List<Elements...>, std::integer_sequence<std::size_t, indices...>>
	{
		using original = List<Elements...>;
		using type = List<list_element_t<indices, original>...>;
	};
	template <typename List, typename Range>
	using collect_list_t = typename collect_list<List, Range>::type;;

	template <typename List>
	struct list_constructor;
	template <template <typename...> class ListCons, typename... Ts>
	struct list_constructor<ListCons<Ts...>>
	{
		template <typename... AnyParams>
		using function = ListCons<AnyParams...>;
	};

	template <typename... Types> struct list
	{
		constexpr list() noexcept {}
		template <typename... Args,
			std::enable_if_t<(std::is_same_v<Args, Types> && ...)>* = nullptr>
		constexpr list(Args&&...) noexcept {}
	};
	template <typename... Args> list(Args&&...) -> list<Args...>;

} // namespace simple::support::meta

#endif /* end of include guard */
